package org.study.redis.boot.config;

import cn.hutool.core.util.ObjectUtil;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.objenesis.strategy.StdInstantiatorStrategy;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

/**
 * 自定义 Kryo 序列化器
 * 参考自 https://blog.csdn.net/2401_86544677/article/details/145919909
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2025年09月30日
 */
public class KryoRedisSerializer implements RedisSerializer<Object> {
    private static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        // Kryo在反射创建实例时，用到了ReflectASM，而ReflectASM要求目标类必须是public且不能是内部类(或非static嵌套类)，否则就会报错
        // 解决方式一：让Kryo禁用ReflectASM，使用标准反射
        // 解决方式二：把要序列化的类拆成独立文件且public；或者保持嵌套但加static和public
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
        // 允许未注册类
        kryo.setRegistrationRequired(false);
        // 支持循环引用
        kryo.setReferences(true);
        return kryo;
    });

    @Override
    public byte[] serialize(Object value) throws SerializationException {
        if (ObjectUtil.isEmpty(value)) {
            return new byte[0];
        }
        try (Output output = new Output(1024, -1)) {
            Kryo kryo = KRYO_THREAD_LOCAL.get();
            kryo.writeClassAndObject(output, value);
            return output.toBytes();
        } catch (Exception e) {
            throw new SerializationException("Kryo 序列化异常", e);
        }
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if (ObjectUtil.isEmpty(bytes)) {
            return null;
        }
        try (Input input = new Input(bytes)) {
            Kryo kryo = KRYO_THREAD_LOCAL.get();
            return kryo.readClassAndObject(input);
        } catch (Exception e) {
            throw new SerializationException("Kryo 反序列化异常", e);
        }
    }
}
